package com.example.parking.manager;

import com.example.parking.ParkingException;
import com.example.parking.Token;
import com.example.parking.boy.CarParkable;
import com.example.parking.boy.ParkingBoy;
import com.example.parking.car.Car;
import com.example.parking.director.Reportable;
import com.example.parking.lot.ParkingLotGroup;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

public abstract class ParkingManager implements Reportable {

    protected List<CarParkable> carParkables;
    protected ParkingLotGroup parkingLotGroup;

    public ParkingManager(ParkingLotGroup parkingLotGroup) {
        this.carParkables = new ArrayList<>();
        this.parkingLotGroup = parkingLotGroup;
    }

    public ParkingManager(ParkingLotGroup parkingLotGroup, List<CarParkable> carParkables) {
        this.carParkables = carParkables;
        this.parkingLotGroup = parkingLotGroup;
    }

    abstract protected CarParkable retrieveCarParker(Car car);

    public Optional<Token> assignAndPark(Car car) {
        return assignParkingMission(retrieveCarParker(car)).apply(car);
    }

    protected void addParkingBoys(ParkingBoy parkingBoy, ParkingLotGroup parkingLotGroup){
        parkingBoy.assignParkingLotGroup(parkingLotGroup);
        this.carParkables.add(parkingBoy);
    }

    protected Function<Car, Optional<Token>> assignParkingMission(CarParkable carParkable){
        return car -> {
            try {
                return Optional.ofNullable(carParkable.park(car));
            } catch (ParkingException e) {
                parkingFailedCallBack(carParkable, car);
                return Optional.empty();
            }
        };
    }

    public void showReport(){
        this.carParkables.forEach(CarParkable::showReport);
    }


    protected void parkingFailedCallBack(CarParkable carParkable, Car car){
        //TODO: apologize
    }

}
